package goiot.api;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import goiot.GoiotHttp;
import goiot.dto.IMessage;
import goiot.dto.MessageAlarm;
import goiot.dto.MessageEvent;
import goiot.dto.MessageOffline;
import goiot.dto.MessageOnline;
import goiot.dto.MessageProperties;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.Response;
import okhttp3.WebSocket;

public class EventbusApi {
	Logger logger = Logger.getGlobal();

	public EventbusApi() {
	}

	/**
	 * 按产品订阅消息
	 * 
	 * @param productId
	 * @param deviceId
	 * @param type
	 * @param cons
	 * @return
	 */
	public void product(String productId, String deviceId, IMessage.Type type, WebSocketListener l) {
		OkHttpClient client = GoiotHttp.getClient();
		String host = getHost();
		host = host + String.format("/api/eventbus/%s/%s/%s", productId, deviceId, type.getName());
		Builder bu = new Request.Builder().url(host);
		bu.addHeader(GoiotHttp.X_ACCESS_TOKEN, GoiotHttp.getToken());
		client.newWebSocket(bu.build(), newListener(l));
	}

	/**
	 * 按设备订阅消息
	 * 
	 * @param deviceId
	 * @param type     [property, online, offline, event, alarm, **]
	 * @param cons
	 * @return
	 */
	public void device(String deviceId, IMessage.Type type, WebSocketListener l) {
		OkHttpClient client = GoiotHttp.getClient();
		String host = getHost();
		host = host + String.format("/api/eventbus/%s/%s", deviceId, type.getName());
		Builder bu = new Request.Builder().url(host);
		bu.addHeader(GoiotHttp.X_ACCESS_TOKEN, GoiotHttp.getToken());
		client.newWebSocket(bu.build(), newListener(l));
	}

	private okhttp3.WebSocketListener newListener(WebSocketListener l) {
		return new okhttp3.WebSocketListener() {
			@Override
			public void onClosed(WebSocket webSocket, int code, String reason) {
				l.onClosed(webSocket, code, reason);
			}

			@Override
			public void onFailure(WebSocket webSocket, Throwable t, Response response) {
				l.onFailure(webSocket, t, response);
			}

			@Override
			public void onMessage(WebSocket webSocket, String text) {
				IMessage msg = convert(text);
				if (null != msg) {
					l.onMessage(webSocket, msg);
				}
			}

			@Override
			public void onOpen(WebSocket webSocket, Response response) {
				l.onOpen(webSocket, response);
			}
		};
	}

	public String getHost() {
		String host = GoiotHttp.getHost();
		if (host.startsWith("http://")) {
			host = host.replace("http:", "ws:");
		} else if (host.startsWith("https://")) {
			host = host.replace("https:", "wss:");
		}
		return host;
	}

	private IMessage convert(String text) {
		ObjectMapper objectMapper = GoiotHttp.getObjectMapper();
		JsonNode node;
		try {
			node = objectMapper.readTree(text);
			String type = node.get("type").textValue();
			if ("property".equals(type)) {
				return objectMapper.treeToValue(node, MessageProperties.class);
			} else if ("event".equals(type)) {
				return objectMapper.treeToValue(node, MessageEvent.class);
			} else if ("online".equals(type)) {
				return objectMapper.treeToValue(node, MessageOnline.class);
			} else if ("offline".equals(type)) {
				return objectMapper.treeToValue(node, MessageOffline.class);
			} else if ("alarm".equals(type)) {
				return objectMapper.treeToValue(node, MessageAlarm.class);
			} else {
				logger.log(Level.INFO, "unkwon type: " + type + "; json text: " + text);
				return null;
			}
		} catch (JsonProcessingException e) {
			logger.log(Level.WARNING, text + ": " + e.getMessage(), e);
		}
		return null;
	}

	public static abstract class WebSocketListener {
		Logger logger = Logger.getGlobal();

		public void onClosed(WebSocket webSocket, int code, String reason) {
			logger.log(Level.INFO, "websocket closed: code: " + code + ", reason: " + reason);
		}

		public void onFailure(WebSocket webSocket, Throwable t, Response response) {
			logger.log(Level.WARNING, "websocket onFailure: ", t);
		}

		public void onOpen(WebSocket webSocket, Response response) {
			logger.log(Level.INFO, "websocket open url: " + response.request().url());
		}

		public abstract void onMessage(WebSocket webSocket, IMessage msg);

	}

}
